/**
 * Copyright (c) 2006, Reliable Source, Inc. 
 * All Rights Reserved.
 */
package org.reliablesource.prism.ui.aspects;

import java.awt.Point;
import java.util.Calendar;
import java.util.Stack;
import java.util.TimeZone;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.CoolItem;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.swt.widgets.Widget;
import org.reliablesource.prism.core.IPrismScreen;
import org.reliablesource.prism.core.IPrismElementFactory;
import org.reliablesource.prism.core.plugin.PrismCorePlugin;
import org.reliablesource.prism.ui.plugin.PrismPlugin;

/**
 * 
 * EventCaptureAspect.aj
 * 
 * Created on: Sep 25, 2006
 * 
 * @author Dennis Park <a
 *         href="mailto:dennis.park@gmail.com">dennis.park@gmail.com</a>
 * 
 * @since 1.0
 */
public aspect EventCaptureAspect {

	protected final String fileType = "png";

	private Point currentLocation;

	boolean isMain = true;

	protected Image image = null;

	protected GC gc = null;

	protected String fileName = PrismPlugin.EMPTY_STRING;

	protected Stack <Event> contextEventStack = new Stack<Event> ();
	protected StringBuffer widgetName;

	pointcut captureEvent(final Event event) : 
		execution(void *handleEvent(Event)) && args(event);

	before(final Event event) : captureEvent(event){
		
		widgetName = new StringBuffer();
		
//		if(event.type == SWT.Activate || event.type == SWT.Dispose){
//			if(event.widget instanceof Shell){
//				String selectionWidget = ((Shell)event.widget).getText();
//				widgetName.append("-=$" + "Shell" + "$=-"+ selectionWidget);
//			}
//		} else 
			
			if (event.type == SWT.Arm || event.type == SWT.Selection
				|| event.type == SWT.KeyUp) {

			String selectionWidget = "";
			
			if(event.type == SWT.Selection){
				if (event.widget instanceof Composite) {

					if (event.widget instanceof CTabFolder) {
						CTabItem selection = ((CTabFolder)event.widget).getSelection();
						selectionWidget = selection.getText();
					} else if(event.widget instanceof ToolBar){
						
						ToolItem [] _items = ((ToolBar)(event.widget)).getItems();
						
						int _nItems = _items.length;
						
						for(int i =0; i < _nItems; i++){
							if(_items[i].getSelection()){
								selectionWidget +=
								_items[i].getText();
								break;
							}
						}
					}else {
						// TabFolder, Tree, Table
						Item[] selection = (Item[]) PrismPlugin
								.inputObjectMethodReflection(event.widget,
										"getSelection", null);
						int nSelections = selection.length;
						
							for(int i =0; i < nSelections; i++){
								selectionWidget+=
								selection[i].getText();
							}
					}
				}else{
					selectionWidget = (String) PrismPlugin.inputObjectMethodReflection(event.widget, "getText", null);
					if (selectionWidget.length() < 1)
						selectionWidget = event.widget.toString();
			}
		}
			else if(event.type == SWT.KeyUp){
				selectionWidget = "'"+event.character + "'";
			}
			else{				
			selectionWidget = (String) PrismPlugin.inputObjectMethodReflection(event.widget, "getText", null);
		}
		
			if (selectionWidget.length() < 1)
				selectionWidget = event.widget.toString();
			
			widgetName.append(selectionWidget);			
		}
	}
	
	after(final Event event) :captureEvent(event){
		try{
			
			if(event.type == SWT.Activate){
				if(!(event.widget instanceof Shell))
					return;
			}
			
			Stack<Event> _eventStack = PrismPlugin.getDefault().getWidgetContext().get(event.widget);
			if (_eventStack != null){
				Event _previous = _eventStack.peek();
				if (null != _previous) {
					if (event.type != _previous.type){	// event type is different 
							_eventStack.push(event);
					}
					else if ((event.time - _previous.time) >= 777){	//	if same, difference should be more than 1/2 second.
							_eventStack.push(event);
					}else{
						return;
					}
				}else{
					_eventStack.push(event);
				}
			}else{
				_eventStack = new Stack<Event>();
				_eventStack.push(event);
				PrismPlugin.getDefault().getWidgetContext().put(event.widget , _eventStack);
			}
			
			if(event.type == SWT.Activate){
				Display.getDefault().asyncExec(new Runnable(){
					public void run(){
						while((!((Control)event.widget).isVisible()))
							Display.getDefault().readAndDispatch();
						
						String selectionWidget = (String) PrismPlugin.inputObjectMethodReflection(event.widget, "getText", null);
						widgetName.append(selectionWidget);		
						
						doCapture(event, widgetName.toString().trim());
						return;
					}
			});
			}
			else if(event.type == SWT.Dispose){
				if(event.widget instanceof Shell ||
					event.widget instanceof Menu	){
					String _path = getControlContextPath(event.widget);
					System.err.println("@@@@@@@@@@");
					System.err.println(_path);
					System.err.println("@@@@@@@@@@");
					
				}
				
				PrismPlugin.getDefault().getWidgetContext().get(event.widget).clear();
				PrismPlugin.getDefault().getWidgetContext().remove(event.widget);
			}else{
					switch(event.type){
					case SWT.MenuDetect:						
						break;
					case SWT.Selection:
						System.err.println("@@@");
						StringBuffer _buffer = new StringBuffer();
						_buffer.append("Select ");
						
						Widget _w = event.widget;
						Widget _parent = (Widget)PrismPlugin.inputObjectMethodReflection(_w, "getParent", null);
						
						_buffer.append(getControlContextPath(_parent));
						System.err.println("@@@");
						break;
					case SWT.Arm:
						break;
					case SWT.MouseDown:
						break;		
					case SWT.FocusIn:
						break;		
					case SWT.KeyUp:
						break;				
					}
					System.err.println("%%");
					System.err.println(PrismPlugin.getDefault().getEventLiteral(event.type));
					System.err.println("%%");
					runCapture(event, widgetName.toString().trim());
			}
				
				
		}
		finally{
			
			Display.getDefault().readAndDispatch();
			if(image != null)
				image.dispose();
			if(gc != null)
				gc.dispose();
			
			Display.getDefault().readAndDispatch();
//			CaptureScreenAction.addDisplayFilter(event.type);
			
			System.err.println("------------END-----------");
			System.err.println("");
		}
	}
	
	/**
	 * Callers should be aware that this method calls <code>doCapture</code>
	 * 	synchronously on the UI thread.
	 */
	private void runCapture(final Event event, final String _name){
			Display.getDefault().syncExec(new Runnable(){
			public void run(){
				Display.getDefault().readAndDispatch();
				doCapture(event, _name);
			}
	});
	}
	
	protected String getControlContextPath(Widget _control){
		
		if(null == _control)
			return "null control";
		
		if(_control instanceof Composite){
			Control [] _children = ((Composite)_control).getChildren();
			String _path = "";
			
			for(int i = 0; i < _children.length; i++)
				_path += getControlContextPath(_children[i]);
			
			return _path;
		}else if(_control instanceof Menu){
			Widget [] _children = ((Menu)_control).getItems();
			String _path = "";
			
			for(int i = 0; i < _children.length; i++)
				_path += getControlContextPath(_children[i]);
			
			return _path;
		}
			String _retVal = (String) PrismPlugin.inputObjectMethodReflection(_control, "getText", null);
			if (_retVal.length() < 1)
				_retVal = _control.toString();
			
			return _retVal;
	}

	private void doCapture(Event _event, String widgetName) {
		Shell activeShell = Display.getDefault().getActiveShell();
		Rectangle shellBounds = activeShell.getBounds();
		boolean isDialog = PrismPlugin.getDefault().isDialog(activeShell);

		if (isDialog) {
			Shell _mainShell = PrismPlugin.getDefault().getMainShell(activeShell);
			shellBounds = _mainShell.getBounds();
		}

		image = new Image(Display.getDefault(), shellBounds);

		/* Take the screen shot */
		gc = new GC(Display.getDefault());
		gc.copyArea(image, shellBounds.x, shellBounds.y);
		ImageData _imgData = image.getImageData();

		Calendar _cal = Calendar.getInstance();
		long captureTime = _cal.getTimeInMillis() - _event.time;
		_cal.setTimeZone(TimeZone.getTimeZone(System
				.getProperty("user.timezone")));
		_cal.setTimeInMillis(_event.time + captureTime);
		int _hour = _cal.get(Calendar.HOUR_OF_DAY);
		int _min = _cal.get(Calendar.MINUTE);
		int _sec = _cal.get(Calendar.SECOND);
		int _mSec = _cal.get(Calendar.MILLISECOND);

		//--------FIXME--Use RegEx to clean up the widget label.
		try {
			if (widgetName != null) {
				if (widgetName.contains("\t"))
					widgetName = widgetName.replaceAll("\t", "_");
				if (widgetName.contains("&"))
					widgetName = widgetName.replaceAll("&", "_");
				if (widgetName.contains("\\+"))
					widgetName = widgetName.replaceAll("\\+", "~");
				if (widgetName.contains(">"))
					widgetName = widgetName.replaceAll(">", "_]");
				if (widgetName.contains("<"))
					widgetName = widgetName.replaceAll("<", "[_");
				if (widgetName.contains(" "))
					widgetName = widgetName.replaceAll(" ", "");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		fileName = _hour + "_" + _min + "_" + _sec + "_" + _mSec + "  -="
				+ PrismPlugin.getDefault().getEventLiteral(_event.type) + "--" 
				+ PrismPlugin.getDefault().getWidgetType(_event.widget) + "=-  "
				+ widgetName;
		new SaveThread(_imgData, fileName, fileType).start();

		// Screen shot scope set to Workbench
		Rectangle clientArea = activeShell.getClientArea();

		int trimX = 0;
		int trimY = 0;

		int dlgConstX = 0;
		int dlgConstY = 0;

		int dlgTrimX = 0;
		int dlgTrimY = 0;

		if (isDialog) {
			dlgConstX = activeShell.getLocation().x;
			dlgConstY = activeShell.getLocation().y;

			dlgTrimX = activeShell.getBounds().x
					- activeShell.getClientArea().x;
			dlgTrimY = activeShell.getBounds().y
					- activeShell.getClientArea().y;
		}

		trimX = shellBounds.x - clientArea.x;
		trimY = shellBounds.y - clientArea.y;

		currentLocation = new Point(Display.getDefault().getCursorLocation().x
				- trimX + dlgConstX + dlgTrimX, Display.getDefault()
				.getCursorLocation().y
				- trimY + dlgConstY + dlgTrimY);

		IPrismElementFactory _factory = PrismCorePlugin.getDefault()
				.getElementFactory();
		IPrismScreen _screen = _factory.createScreen();

		if ((_event.type == SWT.MouseDown || _event.type == SWT.Activate || _event.type == SWT.Deactivate)) {
			_screen.setPointerStart(new Point(_event.x - trimX + dlgConstX,
					_event.y - trimY + dlgConstY));
			currentLocation.x = _event.x - trimX + dlgConstX;
			currentLocation.y = _event.y - trimY + dlgConstY;
		} else
			_screen.setPointerStart(currentLocation);

		_screen.setWidget(_event.widget);
		_screen.setName(widgetName);
		_screen.setImageFileType(fileType);
		_screen.setImageFilePath(PrismPlugin.getDefault().getSavePath());
		_screen.setEventType(_event.type);
		_screen.setEventWidget(_event.widget.toString());
		_screen.setEventDate(_event.time);
	}
}
